/*
 * Lottery.java
 *
 * Created on Aug 18, 2009, 9:16:06 PM
 */

package game;
import main.CSVReader;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.MemoryImageSource;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.Timer;

/**
 * Lottery game. Like questionnaire, the subject gets asked a series of
 * questions. They can either take a guaranteed amount, or they can roll a die
 * for a different payout.
 *
 * @author Justin
 */
public class Lottery extends Game {	
	// VARIABLES ///////////////////////////////////////////////////////////////
	/** The CSV file with the game. */
	String dataFile;

	/** Whether or not we show the subject the outcome of their choices. */
	boolean showResults;

	/** How many sides there are to the die we're rolling. */
	final int SIDES_TO_DIE = 10;

	// CSV file constants.
	final int CSV_GUARANTEE = 0;
	final int CSV_DICEROLL = 1;
	final int CSV_GAMBLE = 2;

	/** The amount of time the subject has to play the game. */
	final int GAME_TIME = 60;

	/** Which question we're on right now, starts at 0. */
	int currentQuestion;

	/** The (smaller) amount the subject could get right away, indexed by
	 question number. */
	ArrayList<Double> guaranteedPayouts = new ArrayList<Double>();

	/** Must roll this number or lower on a 10 sided dice to win gamble. Indexed
	 by question number. */
	ArrayList<Integer> diceRolls = new ArrayList<Integer>();

	/** The (larger) amount the subject gets for winning the dice roll. Indexed
	 by question number. */
	ArrayList<Double> gamblePayouts = new ArrayList<Double>();

	/** Timer for the game countdown. */
    Timer timer = new Timer (1000, new TimeListener());

	/** The current state we're in - asking for input, or providing feedback. */
	int gameState;

	/** The current roll of the die. */
	int dieValue;

	/** Whether the user just left clicked or not. */
	boolean leftClicked;

	/** Amount won on the current question. */
	double currentAmountWon;

	/** Amount won this session. */
	double sessionAmountWon;

	/** Amount of time, in seconds, the subject spent making their decision. */
	int playerThinkTime = 0;

	// Game state constants.
	final int GS_QUESTION = 0;
	final int GS_FEEDBACK = 1;

	// End game statuses.
	final int EG_NORMAL = 0;
	final int EG_BADFILE = 1; // Something was wrong with the CSV file.
	final int EG_TIMEOUT = 2;
	
	// Results constants.
	final int RES_TAKE = 1;
	final int RES_ROLL = 2;
	final int RES_ROLL_WIN = 2;
	final int RES_ROLL_LOSE = 3;

	// METHODS /////////////////////////////////////////////////////////////////
    /**
	 * Constructor.
	 */
    public Lottery (String params, int currentRep) {
        super();

		initComponents();
		this.setVisible (true);
        hideMouse();
		
		runner.addMouseListener (new Lottery.CheckMouse());
        this.addMouseListener (new Lottery.CheckMouse());
		this.getGlassPane().addMouseListener(new Lottery.CheckMouse());

		String paramPart[] = params.split (",");
        dataFile = paramPart[2];
		showResults = paramPart[3].toLowerCase().equals("true");
		this.params = params;
		repTot = Integer.parseInt (paramPart[1]);
		repNum = currentRep;

		secs = GAME_TIME;
		updateTimerLabel();
		timer.start();
		getData();

		gameState = GS_QUESTION;
		sessionAmountWon = 0;
		currentQuestion = 0;
		displayQuestion (currentQuestion);
    }

	// GAME ////////////////////////////////////////////////////////////////////
	/**
     * Defines what we do when the user clicks the mouse.
     */
    class CheckMouse implements MouseListener{
		public void mousePressed (MouseEvent e) { hideMouse(); }
		public void mouseReleased (MouseEvent e) {}
		public void mouseEntered (MouseEvent e) {}
		public void mouseExited (MouseEvent e) {}

        /**
         * Handle mouse clicks.
         *
         * @param e The MouseEvent.
         */
		public void mouseClicked (MouseEvent e){
			int modifiers = e.getModifiers();

			if (gameState == GS_QUESTION) {
				// Left click.
				if ((modifiers & InputEvent.BUTTON1_MASK) ==
							InputEvent.BUTTON1_MASK) {
					leftClicked = true;
					currentAmountWon = guaranteedPayouts.get(currentQuestion);
					sessionAmountWon += currentAmountWon;
					totalMoneyWon += currentAmountWon;

					writeGameLog (playerId + "," + playerThinkTime + ","
						+ dataFile + "," + (currentQuestion+1) + "," + RES_TAKE
						+ "," + RES_TAKE + ","
						+ String.format("$%.2f", currentAmountWon));
					playerThinkTime = 0;
				}
				// Right click.
				else if ((modifiers & InputEvent.BUTTON3_MASK) ==
							InputEvent.BUTTON3_MASK) {
					leftClicked = false;
					currentAmountWon = 0;
					dieValue = new Random().nextInt (SIDES_TO_DIE) + 1;
					if (dieValue <= diceRolls.get(currentQuestion)) {
						currentAmountWon = gamblePayouts.get (currentQuestion);
						sessionAmountWon += currentAmountWon;
						totalMoneyWon += currentAmountWon;

						writeGameLog (playerId + "," + playerThinkTime + ","
							+ dataFile + "," + (currentQuestion+1) + ","
							+ RES_ROLL + "," + RES_ROLL_WIN + ","
							+ String.format("$%.2f", currentAmountWon));
						playerThinkTime = 0;
					}
					else {
						writeGameLog (playerId + "," + playerThinkTime + ","
							+ dataFile + "," + (currentQuestion+1) + ","
							+ RES_ROLL + "," + RES_ROLL_LOSE + ",$0.00");
						playerThinkTime = 0;
					}
				}
				else {}
			}

			if (!showResults || gameState == GS_FEEDBACK)
				currentQuestion++;
			if (showResults)
				gameState = 1 - gameState;

			if (currentQuestion < guaranteedPayouts.size()) {
				// Left or right click.
				if (((modifiers & InputEvent.BUTTON1_MASK) ==
						InputEvent.BUTTON1_MASK)
					|| ((modifiers & InputEvent.BUTTON3_MASK) ==
						InputEvent.BUTTON3_MASK)) {
					if (gameState == GS_QUESTION) {
						secs = GAME_TIME;
						updateTimerLabel();
						timer.start();
						displayQuestion (currentQuestion);
					}
					else {
						timer.stop();
						displayFeedback (currentQuestion);
					}
				}

				// Invalid input.
				else {}
			}
			else
				endGame (EG_NORMAL);
        }
    }

	/**
     * Handles game timer actions.
     */
    class TimeListener implements ActionListener {
    	/**
         * Decrement the time remaining, if there is any. If the user has run
         * out of time, then record the amount of money won and exit.
         *
         * @param e The ActionEvent.
         */
        public void actionPerformed(ActionEvent e) {
            if (secs > 0) {
                secs--;
				playerThinkTime++;
                updateTimerLabel();
            }
            else {
                timer.stop();
                statusLabel.setText ("Sorry, your time is up.");
				secureOption.setVisible (false);
				gambleOption.setVisible (false);
				jLayeredPane1.setVisible (false);
				jLayeredPane2.setVisible (false);
				writeGameLog (playerId + "," + playerThinkTime + ","
					+ dataFile + "," + (currentQuestion+1) + ",TimeOut,TimeOut,"
					+ "TimeOut");
                endGame (EG_TIMEOUT);
            }
    	}
    }

	/**
     * Based on the time remaining, updates the time remaining label.
     */
    private void updateTimerLabel() {
        int min = secs / 60;
        int sec = secs % 60;
        timeLabel.setText (min + ":" + ((sec < 10) ? "0" : "") + sec);
    }

	/**
     * Gets the questions and possible answers from the data file.
     */
    private void getData() {
        try {
            CSVReader reader = new CSVReader (new FileReader
                    (new File ("lotteries/" + dataFile)));
            ArrayList<ArrayList<String>> lotteryData =
                    (ArrayList<ArrayList<String>>) reader.readAll();

			if (lotteryData.size() < 1) {
				System.err.println ("[ERROR]: Lottery file has no" +
						" entries (" + dataFile + ").");
				endGame (EG_BADFILE);
			}

            // No labels, so we start at 0.
            for (int i=0; i<lotteryData.size(); i++) {
                guaranteedPayouts.add (Double.parseDouble(
						lotteryData.get(i).get(CSV_GUARANTEE).trim()));
                diceRolls.add (Integer.parseInt(
						lotteryData.get(i).get(CSV_DICEROLL).trim()));
                gamblePayouts.add (Double.parseDouble(
						lotteryData.get(i).get(CSV_GAMBLE).trim()));
            }
        }
        catch (Exception e) {
            System.err.println ("[ERROR]: Error while trying to open the "
					+ "lottery data file (" + dataFile + ").");
            e.printStackTrace();
        }
    }

	/**
	 * Ends the game.
	 */
	public void endGame (int status) {
		String payment = "";
		String opponentPay = "";
		
		if (status == EG_NORMAL)
			payment = String.format("$%.2f", sessionAmountWon);
		else if (status == EG_BADFILE) {
			payment = "BadFile";
			opponentPay = "BadFile";
		}
		else if (status == EG_TIMEOUT) {
			payment = "TimeOut";
			opponentPay = "TimeOut";
		}
		else {}
		
		writeLog (playerId + ",,\"" + params + "\"," + repNum + "," + repTot
			+ "," + payment + "," + opponentPay);
		setVisible (false);
		free = true;
	}

	// LAYOUT //////////////////////////////////////////////////////////////////
	/**
     * Updates the layout to display the given question.
     * @param currentQuestion
     *          The index in the questions array we want to display.
     */
    private void displayQuestion (int currentQuestion) {
        if (currentQuestion >= guaranteedPayouts.size())
			return;

		statusLabel.setText ("You can choose one of the following:");
		secureOption.setText ("<html><p>Take "
			+ String.format ("$%.2f", guaranteedPayouts.get(currentQuestion))
			+ ".</p></html>");
		secureOption.setVisible (true);
		gambleOption.setText ("<html><p>Roll the " + SIDES_TO_DIE + "-sided"
			+ " die, and if it comes up " + diceRolls.get(currentQuestion)
			+ " or lower, you will receive "
			+ String.format ("$%.2f", gamblePayouts.get(currentQuestion))
			+ ".</p></html>");
		gambleOption.setVisible (true);
		takeText.setText ("Take " + String.format ("$%.2f",
			guaranteedPayouts.get(currentQuestion)));
		rollText.setText ("Roll for " + String.format ("$%.2f",
			gamblePayouts.get(currentQuestion)));
		jLayeredPane1.setVisible (true);
		jLayeredPane2.setVisible (true);
	}

	/**
     * Updates the layout to display the result of the previous decision.
	 * 
     * @param currentQuestion
     *          The index in the questions array we want to display.
     */
    private void displayFeedback (int currentQuestion) {
        if (currentQuestion >= guaranteedPayouts.size())
			return;

		String statusText = "";
		if (leftClicked) {
			statusText += "You took " + String.format ("$%.2f",
					currentAmountWon) + ".";
		}
		else {
			statusText += "You rolled a " + dieValue + ", winning you "
				+ String.format ("$%.2f", currentAmountWon) + ".";
		}
		statusLabel.setText ("<html><div align='center'><p>" + statusText
			+ "<br>Click to continue.</p></div></html>");
		secureOption.setVisible (false);
		gambleOption.setVisible (false);
		jLayeredPane1.setVisible (false);
		jLayeredPane2.setVisible (false);
	}

	/**
     * Hides the mouse so the user can't see it.
     */
    private void hideMouse() {
        int[] pixels = new int[16 * 16];
        Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
        Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "invisibleCursor");
        this.getGlassPane().setCursor(transparentCursor);
		this.getGlassPane().setVisible(true);
        runner.setCursor(transparentCursor);
    }

	// GENERATED CODE AND MAIN /////////////////////////////////////////////////
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        titleLabel = new javax.swing.JLabel();
        statusLabel = new javax.swing.JLabel();
        secureOption = new javax.swing.JLabel();
        gambleOption = new javax.swing.JLabel();
        jLayeredPane1 = new javax.swing.JLayeredPane();
        takeText = new javax.swing.JLabel();
        takeButton = new javax.swing.JLabel();
        jLayeredPane2 = new javax.swing.JLayeredPane();
        rollText = new javax.swing.JLabel();
        rollButton = new javax.swing.JLabel();
        timeLabel = new javax.swing.JLabel();

        setBackground(new java.awt.Color(255, 255, 255));
        setBorder(null);
        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setMaximumSize(new java.awt.Dimension(800, 600));
        setMinimumSize(new java.awt.Dimension(800, 600));
        setPreferredSize(new java.awt.Dimension(800, 600));

        titleLabel.setFont(new java.awt.Font("Calibri", 1, 28)); // NOI18N
        titleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        titleLabel.setText("Lottery");

        statusLabel.setFont(new java.awt.Font("Calibri", 1, 28)); // NOI18N
        statusLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        statusLabel.setText("You can choose one of the following:");

        secureOption.setFont(new java.awt.Font("Calibri", 0, 28)); // NOI18N
        secureOption.setText("Take $1.");
        secureOption.setVerticalAlignment(javax.swing.SwingConstants.TOP);

        gambleOption.setFont(new java.awt.Font("Calibri", 0, 28)); // NOI18N
        gambleOption.setText("Roll the 10-sided die, and if it comes up 5 or lower, you will $2");
        gambleOption.setVerticalAlignment(javax.swing.SwingConstants.TOP);

        jLayeredPane1.setMaximumSize(new java.awt.Dimension(233, 75));
        jLayeredPane1.setMinimumSize(new java.awt.Dimension(233, 75));

        takeText.setFont(new java.awt.Font("Tahoma", 1, 24)); // NOI18N
        takeText.setForeground(new java.awt.Color(255, 255, 255));
        takeText.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        takeText.setText("Take $");
        takeText.setBounds(20, 10, 230, 60);
        jLayeredPane1.add(takeText, javax.swing.JLayeredPane.DEFAULT_LAYER);

        takeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/greenBtnSmall.png"))); // NOI18N
        takeButton.setBounds(20, 0, 240, 75);
        jLayeredPane1.add(takeButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        rollText.setFont(new java.awt.Font("Tahoma", 1, 24)); // NOI18N
        rollText.setForeground(new java.awt.Color(255, 255, 255));
        rollText.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        rollText.setText("Roll for $");
        rollText.setBounds(20, 10, 210, 60);
        jLayeredPane2.add(rollText, javax.swing.JLayeredPane.DEFAULT_LAYER);

        rollButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/greenBtnSmall.png"))); // NOI18N
        rollButton.setBounds(10, 0, 240, 75);
        jLayeredPane2.add(rollButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        timeLabel.setFont(new java.awt.Font("Calibri", 1, 36)); // NOI18N
        timeLabel.setForeground(new java.awt.Color(153, 102, 0));
        timeLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/hrGlass.png"))); // NOI18N
        timeLabel.setText("3:00");

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(titleLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
            .add(timeLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, statusLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 113, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .add(secureOption, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 161, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .add(113, 113, 113)
                        .add(gambleOption, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 330, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .add(63, 63, 63)))
                .addContainerGap())
            .add(layout.createSequentialGroup()
                .add(48, 48, 48)
                .add(jLayeredPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 277, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .add(91, 91, 91)
                .add(jLayeredPane2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 279, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(105, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .add(timeLabel)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(titleLabel)
                .add(42, 42, 42)
                .add(statusLabel)
                .add(18, 18, 18)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(gambleOption, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 142, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(secureOption, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 94, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .add(112, 112, 112)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(jLayeredPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 84, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jLayeredPane2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 84, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .add(85, 85, 85))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        /*java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Lottery().setVisible(true);
            }
        });*/
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel gambleOption;
    private javax.swing.JLayeredPane jLayeredPane1;
    private javax.swing.JLayeredPane jLayeredPane2;
    private javax.swing.JLabel rollButton;
    private javax.swing.JLabel rollText;
    private javax.swing.JLabel secureOption;
    private javax.swing.JLabel statusLabel;
    private javax.swing.JLabel takeButton;
    private javax.swing.JLabel takeText;
    private javax.swing.JLabel timeLabel;
    private javax.swing.JLabel titleLabel;
    // End of variables declaration//GEN-END:variables
}